package com.bgyrobot.coordinate.util;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;

public class MathUtil {
    
    /**
     * 保留 n 位有效数字
     * @param num 原数字
     * @param n 位数
     * @return 保留 n 位有效数字结果
     */
    public static double significantDigits(double num, int n) {
        return new BigDecimal(num, new MathContext(n, RoundingMode.HALF_UP)).doubleValue();
    }
    
    /**
     * 对所有数组数字保留 n 位有效数字
     */
    public static void significantDigits(double[] matrix, int n) {
        for (int i = 0; i < matrix.length; i++) {
            matrix[i] = significantDigits(matrix[i], n);
        }
    }

    /**
     * 对所有数组数字保留 n 位有效数字
     */
    public static void significantDigits(double[][] matrix, int n) {
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                matrix[i][j] = significantDigits(matrix[i][j], n);
            }
        }
    }

    /**
     * 对所有数组数字保留 n 位有效数字
     */
    public static void significantDigits(double[][][] matrix, int n) {
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                for (int k = 0; k < matrix[i][j].length; k++) {
                    matrix[i][j][k] = significantDigits(matrix[i][j][k], n);
                }
            }
        }
    }
    
    /**
     * 保留 n 位小数
     * @param num 原数字
     * @param n 位数
     * @return 保留 n 位小数结果
     */
    public static double scaleDigits(double num, int n) {
        return new BigDecimal(num).setScale(n, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 对所有数组数字保留 n 位小数
     */
    public static void scaleDigits(double[] matrix, int n) {
        for (int i = 0; i < matrix.length; i++) {
            matrix[i] = scaleDigits(matrix[i], n);
        }
    }
    
    /**
     * 对所有数组数字保留 n 位小数
     */
    public static void scaleDigits(double[][] matrix, int n) {
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                matrix[i][j] = scaleDigits(matrix[i][j], n);
            }
        }
    }
    
    /**
     * 对所有数组数字保留 n 位小数
     */
    public static void scaleDigits(double[][][] matrix, int n) {
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                for (int k = 0; k < matrix[i][j].length; k++) {
                    matrix[i][j][k] = scaleDigits(matrix[i][j][k], n);
                }
            }
        }
    }
    
    public static String dataShape(double[] arr) {
        return arr == null ? "with-null" : "" + arr.length;
    }
    
    public static String dataShape(double[][] arr) {
        if (arr == null) {
            return "with-null*?";
        }
        int shape1 = -1;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == null) {
                shape1 = Integer.MIN_VALUE;
                continue;
            }
            
            if (shape1 != Integer.MIN_VALUE && shape1 != Integer.MAX_VALUE) {
                if (shape1 == -1) {
                    shape1 = arr[i].length;
                } else {
                    if (shape1 != arr[i].length) {
                        shape1 = Integer.MAX_VALUE;
                    }
                }
            }
        }

        return String.format("%d*%s", arr.length, shapeString(shape1));
    }
    
    public static String dataShape(double[][][] arr) {
        if (arr == null) {
            return "with-null*?*?";
        }
        int shape1 = -1;
        int shape2 = -1;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == null) {
                shape1 = Integer.MIN_VALUE;
                continue;
            }
            
            if (shape1 != Integer.MIN_VALUE && shape1 != Integer.MAX_VALUE) {
                if (shape1 == -1) {
                    shape1 = arr[i].length;
                } else {
                    if (shape1 != arr[i].length) {
                        shape1 = Integer.MAX_VALUE;
                    }
                }
            }
            
            for (int j = 0; j < arr[i].length; j++) {
                if (arr[i][j] == null) {
                    shape2 = Integer.MIN_VALUE;
                    continue;
                }
                
                if (shape2 != Integer.MIN_VALUE && shape2 != Integer.MAX_VALUE) {
                    if (shape2 == -1) {
                        shape2 = arr[i].length;
                    } else {
                        if (shape2 != arr[i][j].length) {
                            shape2 = Integer.MAX_VALUE;
                        }
                    }
                }
            }
        }
        
        return String.format("%d*%s*%s", arr.length, shapeString(shape1), shapeString(shape2));
    }
    
    private static String shapeString(Integer shape) {
        if (shape == -1) {
            return "?";
        }
        if (shape == Integer.MIN_VALUE) {
            return "with-null";
        }
        if (shape == Integer.MAX_VALUE) {
            return "irreguar";
        }
        return "" + shape;
    }
    
    /**
     * 两个数组结构相同，且精度内相等
     * @return
     */
    public static boolean arrayEqual(double[] arr1, double[] arr2, double precision) {
        if (arr1 == null && arr2 == null) {
            return true;
        }
        if (arr1 == null || arr2 == null) {
            return false;
        }
        if (arr1.length != arr2.length) {
            return false;
        }
        for (int i = 0; i < arr1.length; i++) {
            if (Math.abs(arr1[i] - arr2[i]) > precision) {
                return false;
            }
        }
        return true;
    }

    /**
     * 两个数组结构相同，且精度内相等
     * @return
     */
    public static boolean arrayEqual(double[][] arr1, double[][] arr2, double precision) {
        if (arr1 == null && arr2 == null) {
            return true;
        }
        if (arr1 == null || arr2 == null) {
            return false;
        }
        if (arr1.length != arr2.length) {
            return false;
        }
        for (int i = 0; i < arr1.length; i++) {
            if (! arrayEqual(arr1[i], arr2[i], precision)) {
                return false;
            }
        }
        return true;
    }
    /**
     * 两个数组结构相同，且精度内相等
     * @return
     */
    public static boolean arrayEqual(double[][][] arr1, double[][][] arr2, double precision) {
        if (arr1 == null && arr2 == null) {
            return true;
        }
        if (arr1 == null || arr2 == null) {
            return false;
        }
        if (arr1.length != arr2.length) {
            return false;
        }
        for (int i = 0; i < arr1.length; i++) {
            if (! arrayEqual(arr1[i], arr2[i], precision)) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * 拷贝数组
     */
    public static double[] arrayClone(double[] arr) {
        if (arr == null) {
            return null;
        }
        double[] out = new double[arr.length];
        System.arraycopy(arr, 0, out, 0, arr.length);
        return out;
    }
    
    /**
     * 拷贝数组
     */
    public static double[][] arrayClone(double[][] arr) {
        if (arr == null) {
            return null;
        }
        double[][] out = new double[arr.length][];
        for (int i = 0; i < arr.length; i++) {
            out[i] = arrayClone(arr[i]);
        }
        return out;
    }
    
    /**
     * 拷贝数组
     */
    public static double[][][] arrayClone(double[][][] arr) {
        if (arr == null) {
            return null;
        }
        double[][][] out = new double[arr.length][][];
        for (int i = 0; i < arr.length; i++) {
            out[i] = arrayClone(arr[i]);
        }
        return out;
    }
}
